/*
 * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang.ref;

import sun.misc.Cleaner;
import sun.misc.JavaLangRefAccess;
import sun.misc.SharedSecrets;

/**
 * 引用对象的抽象基类。 此类定义了所有引用对象通用的操作。 由于引用对象是与垃圾收集器密切合作实现的，因此该类可能无法直接子类化。
 * <p> 1、强引用  对象不会被回收
 * <p> 2、软引用  只有在内存不够的情况下，才能被回收
 * <p> 3、弱引用  一定会被回收，它只能存活到下一次垃圾回收之前
 * <p> 4、虚引用  这个对象被回收时收到一个系统通知
 * <p> 5、终引用
 * <p> T 引用类,是所有引用类的父类,定义了强引用之外引用概念
 */

public abstract class Reference<T> {


    /**
     * 类对象关联的具体对象,意味着该对象具有了特殊引用,会收到GC的特殊处理
     */
    private T referent;         /* 经过GC特殊处理 */


    /**
     * 队列, 引用队列，如果引用队列存在，则所指对象被GC回收后，会将对应的引用对象存入到引用队列中
     * <p> 1、如果Referecne（引用）类对象注册了引用队列（即queue（引用队列）字段不为null），则其会在所指对象被GC回收时加入引用队列，
     * 此时开发者就可以通过获取引用队列中Referecne（引用）类对象判断其对应的所指对象是否被GC回收，并同时可以在此时机执行一些自定义的操作。
     * <p>
     * <p> 2、queue（引用队列）字段还被用于与next（下个引用）字段相配合表示Referecne（引用）类对象的状态，这是一个未通过具体字段实现的概念。
     * <p>
     * <p> 3、该值还被用于判断当前Referecne（引用）类对象是否已被加入引用队列中。
     * 当Referecne（引用）类对象被加入/移除其注册的引用队列后，会将该字段值设置为“入队”引用队列/“空”引用队列，因此该字段可作为判断Referecne（引用）类对象是否出/入队的标志
     */
    volatile ReferenceQueue<? super T> queue;

    /**
     * 下个引用
     * 活动     active:   NULL
     * 待定    pending:   this
     * 入队   Enqueued:   next reference in queue (or this if last)
     * 怠惰   Inactive:   this
     */
    @SuppressWarnings("rawtypes")
    volatile Reference next;

    /**
     * active:   由GC负责维护的发现列表中的后继引用对象的引用，如果当前引用对象是发现列表中的最后一个节点，则指向自身；
     * pending:   待定列表中的后继引用对象的引用，如果当前引用对象是待定列表中的最后一个节点，则为null；
     * otherwise:   NULL
     */
    transient private Reference<T> discovered;


    /**
     * 锁类,该字段值用于访问待定列表时作为同步手段的锁对象使用。该字段是一个私有的静态变量，因此可知是一个类锁
     */
    static private class Lock {
    }

    /**
     * 静态类型的锁对象，用于加类锁
     */
    private static Lock lock = new Lock();


    /**
     * 待定引用,待定列表的头Referecne（引用）类对象
     * <p>    待定列表不是一个真正意义上列表对象，而是一个通过链接组成的概念列表，而pending（待定引用）字段便表示其头Referecne（引用）类对象
     * <p> 因此可知只要获得了头Referecne（引用）类对象就相当于获得了整个待定列表
     * <p>    pending（待定引用）字段是一个私有的静态变量，因此可知整个JVM中就只有一个待定列表，且该字段由GC负责赋值。
     * <p>
     * <p> 待定列表用于保存处于active（活动）状态且注册了引用队列的Referecne（引用）类对象。
     * <p> Referecne（引用）类对象一开始会存在于发现列表中，GC会将其中需要执行Referecne（引用）类机制的对象挑选出来置入待定列表中，以供“引用处理器”线程进行处理
     */
    private static Reference<Object> pending = null;










    // 静态代码块


    /**
     * 在Referecne（引用）类的静态块中创建了一条引用处理器线程。
     * <p> 1、该线程是Referecne（引用）类的内部类ReferenceHandler（引用处理器）类的实例，专用于执行Referecne（引用）机制，因此可知整个Referecne（引用）机制都是通过这一条线程来执行的。
     * <p> 2、该线程是一条守护线程，优先级被设置为了10（最高），目的是尽可能多的获取CPU资源。
     */
    static {
        // 获取当前线程所在的线程组。
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent())
            ;

        // 实例化一个引用处理线程，该线程是一个守护线程，并且是最高的优先级。该线程专用于将等待列表中的引用对象添加到引用队列中。
        Thread handler = new ReferenceHandler(tg, "Reference Handler");

        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        // provide access in SharedSecrets
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }












    /* High-priority thread to enqueue pending References
     */
    private static class ReferenceHandler extends Thread {

        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }

        static {
            // pre-load and initialize InterruptedException and Cleaner classes
            // so that we don't get into trouble later in the run loop if there's
            // memory shortage while loading/initializing them lazily.
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

    /**
     * Try handle pending {@link Reference} if there is one.<p>
     * Return {@code true} as a hint that there might be another
     * {@link Reference} pending or {@code false} when there are no more pending
     * {@link Reference}s at the moment and the program can do some other
     * useful work instead of looping.
     *
     * @param waitForNotify if {@code true} and there was no pending
     *                      {@link Reference}, wait until notified from VM
     *                      or interrupted; if {@code false}, return immediately
     *                      when there is no pending {@link Reference}.
     * @return {@code true} if there was a {@link Reference} pending and it
     * was processed, or we waited for notification and either got it
     * or thread was interrupted before being notified;
     * {@code false} otherwise.
     */
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    // 'instanceof' might throw OutOfMemoryError sometimes
                    // so do this before un-linking 'r' from the 'pending' chain...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // The waiting on the lock may cause an OutOfMemoryError
                    // because it may try to allocate exception objects.
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // Give other threads CPU time so they hopefully drop some live references
            // and GC reclaims some space.
            // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
            // persistently throws OOME for some time...
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }



    /* -- Referent accessor and setters -- */

    /**
     * Returns this reference object's referent.  If this reference object has
     * been cleared, either by the program or by the garbage collector, then
     * this method returns <code>null</code>.
     *
     * @return The object to which this reference refers, or
     * <code>null</code> if this reference object has been cleared
     */
    public T get() {
        return this.referent;
    }

    /**
     * Clears this reference object.  Invoking this method will not cause this
     * object to be enqueued.
     *
     * <p> This method is invoked only by Java code; when the garbage collector
     * clears references it does so directly, without invoking this method.
     */
    public void clear() {
        this.referent = null;
    }

    /* -- Queue operations -- */

    /**
     * Tells whether or not this reference object has been enqueued, either by
     * the program or by the garbage collector.  If this reference object was
     * not registered with a queue when it was created, then this method will
     * always return <code>false</code>.
     *
     * @return <code>true</code> if and only if this reference object has
     * been enqueued
     */
    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);
    }

    /**
     * Clears this reference object and adds it to the queue with which
     * it is registered, if any.
     *
     * <p> This method is invoked only by Java code; when the garbage collector
     * enqueues references it does so directly, without invoking this method.
     *
     * @return <code>true</code> if this reference object was successfully
     * enqueued; <code>false</code> if it was already enqueued or if
     * it was not registered with a queue when it was created
     */
    public boolean enqueue() {
        this.referent = null;
        return this.queue.enqueue(this);
    }


    /**
     * Throws {@link CloneNotSupportedException}. A {@code Reference} cannot be
     * meaningfully cloned. Construct a new {@code Reference} instead.
     *
     * @apiNote This method is defined in Java SE 8 Maintenance Release 4.
     *
     * @return  never returns normally
     * @throws  CloneNotSupportedException always
     *
     * @since 8
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    /* -- Constructors -- */

    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
}
